import { observable, computed, reaction, IObservableArray } from 'mobx';
import BookModel, { IBook } from '../models/BookModel'
export interface ITag {
  tagName: string
  count: number
}

interface IStorageModel {
  books: IBook[]
  tags?: ITag[]
}
export default class BookStore {
  public static fromLocalStorge() {
    const str = localStorage.getItem('book-ts-demo')
    let data: IStorageModel = { books: [] }

    /* tslint:disable:no-empty */
    if (str) {
      try {
        data = JSON.parse(str)
      } catch (err) { }
    }
    return BookStore.fromJS(data.books, data.tags)
  }
  private static fromJS(books: IBook[], tags?: ITag[]) {
    const bookStore = new BookStore();
    bookStore.books = books.map(book => BookModel.fromJS(bookStore, book)) as IObservableArray<BookModel>;
    if (tags) {
      tags.forEach((tag) => {
        if (tag.count <= 0) { return }
        bookStore.tags.set(tag.tagName, tag.count)
      })
    }
    return bookStore;
  }

  @observable public books: IObservableArray<BookModel> = observable.array([]);
  @observable public tags = new Map<string, number>()
  @observable public filterTag?: string;

  @computed get filterBooks() {
    if (this.filterTag === undefined) {
      return this.books.sort(sortBook)
    }
    return this.books.filter(book => book.tags.indexOf(this.filterTag!) >= 0).sort(sortBook)

  }
  @computed get starBooks() {
    return this.books.filter(book => book.star >= 4).sort((a, b) => {
      if (a.star === b.star) { return 0 }
      return a.star > b.star ? -1 : 1
    }).slice(0, 6)
  }

  @computed get tagArr() {
    const tags: ITag[] = [];
    this.tags.forEach((count, tagName) => {
      tags.push({ count, tagName })
    })
    return tags.sort((a, b) => {
      if (a.count === b.count) { return 0; }
      return a.count > b.count ? -1 : 1;
    })
  }

  public subscribeLocalstorageToStore() {
    reaction(
      () => this.toJS(),
      data => localStorage.setItem('book-ts-demo', JSON.stringify(data))
    );
  }
  public selectAll() {
    this.filterTag = undefined
  }
  public filter(tag: string) {
    this.filterTag = tag
  }
  public addBook(bookName: string, url?: string) {
    const book: IBook = {
      bookName, url, star: 0
    }
    this.books.push(new BookModel(this, book));
  }
  public changeBook(id: string, bookName: string, url?: string) {
    const book = this.books.find(b => b.id === id)
    if (book) {
      book.change(bookName, url)
    }
  }
  public removeBook(book: BookModel) {
    book.destroy()
  }

  public incTag(tags: string[]) {
    for (const tag of tags) {
      let count = this.tags.get(tag)
      if (count === undefined) { count = 1 }
      else { count++ }
      this.tags.set(tag, count)
    }
  }
  public decTag(tags: string[]) {
    for (const tag of tags) {
      let count = this.tags.get(tag)
      if (count === undefined) { continue }
      count--
      if (count <= 0) { this.tags.delete(tag) }
      else { this.tags.set(tag, count) }
    }
  }

  public toJS() {
    return {
      books: this.books.map(book => book.toJS()),
      tags: this.tagArr
    };
  }
}


function sortBook(a: IBook, b: IBook): number {
  if (a.bookName === b.bookName) { return 0 }
  return a.bookName.localeCompare(b.bookName)
}
